﻿using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using Microsoft.Crm.Sdk.Metadata;

namespace Custom.Crm.General.Utility
{
    public class PropertyUtility
    {
        ICrmService m_crmService = null;
        IMetadataService m_metadataService = null;

        IPluginExecutionContext m_context = null;

        DynamicEntity m_inputEntity = null;
        DynamicEntity m_imagePostEntity = null;
        DynamicEntity m_imagePreEntity = null;

        [Flags]
        public enum ImageType
        {
            Target = 1,
            PreImage = 2,
            PostImage = 4
        }

        public PropertyUtility(IPluginExecutionContext context)
        {
            m_context = context;
            InitalizeDynamicImages(m_context);
        }

        private void InitalizeDynamicImages(IPluginExecutionContext context)
        {
            if (context.InputParameters.Contains("Target") == true)
            {
                if (context.InputParameters.Properties["Target"].GetType() == typeof(DynamicEntity))
                    m_inputEntity = (DynamicEntity)context.InputParameters.Properties["Target"];
                else
                    m_inputEntity = null;
            }
            if (context.PreEntityImages.Contains("Target") == true)
                m_imagePreEntity = (DynamicEntity)context.PreEntityImages.Properties["Target"];

            if (context.PostEntityImages.Contains("Target") == true)
                m_imagePostEntity = (DynamicEntity)context.PostEntityImages.Properties["Target"];
        }

        public ICrmService GetCrmService()
        {
            if (m_crmService == null)
                m_crmService = m_context.CreateCrmService(true);

            return m_crmService;
        }

        public IMetadataService GetMetadataService()
        {
            if (m_metadataService == null)
                m_metadataService = m_context.CreateMetadataService(true);

            return m_metadataService;
        }

        public object GetPropertyFromContext(string fieldName)
        {
            return GetPropertyFromContext(fieldName, ImageType.Target | ImageType.PreImage | ImageType.PostImage);
        }

        public object GetPropertyFromContext(string fieldName, ImageType type)
        {
            if ((type & ImageType.Target) == ImageType.Target)
            {
                if (m_inputEntity != null && m_inputEntity.Properties.Contains(fieldName) == true)
                    return m_inputEntity.Properties[fieldName];
            }

            if ((type & ImageType.PostImage) == ImageType.PostImage)
            {
                if (m_imagePostEntity != null && m_imagePostEntity.Properties.Contains(fieldName) == true)
                    return m_imagePostEntity.Properties[fieldName];
            }

            if ((type & ImageType.PreImage) == ImageType.PreImage)
            {
                if (m_imagePreEntity != null && m_imagePreEntity.Properties.Contains(fieldName) == true)
                    return m_imagePreEntity.Properties[fieldName];
            }

            return null;
        }

        public void CopyAttributeValue(string sourceFieldName, string targetFieldName, ImageType type)
        {
            CopyAttributeValue(m_context, sourceFieldName, targetFieldName, type);
        }

        public void CopyAttributeValue(string sourceFieldName, string targetFieldName)
        {
            CopyAttributeValue(m_context, sourceFieldName, targetFieldName);
        }

        public void CopyAttributeValue(IPluginExecutionContext context, string sourceFieldName, string targetFieldName, ImageType type)
        {
            object prop = GetPropertyFromContext(sourceFieldName, type);
            SetAttributeValueOnTarget(context, targetFieldName, prop);
        }

        public void CopyAttributeValue(IPluginExecutionContext context, string sourceFieldName, string targetFieldName)
        {
            object prop = GetPropertyFromContext(sourceFieldName);
            SetAttributeValueOnTarget(context, targetFieldName, prop);
        }

        public void SetAttributeValueOnTarget(string fieldName, object attributeValue)
        {
            SetAttributeValueOnTarget(m_inputEntity, fieldName, attributeValue);
        }

        public void SetAttributeValueOnTarget(IPluginExecutionContext context, string fieldName, object attributeValue)
        {
            DynamicEntity inputEntity = (DynamicEntity)context.InputParameters.Properties["Target"];
            SetAttributeValueOnTarget(inputEntity, fieldName, attributeValue);
        }

        public void SetAttributeValueOnTarget(DynamicEntity inputEntity, string fieldName, object attributeValue)
        {
            if (inputEntity != null && inputEntity.Properties.Contains(fieldName) == true)
                inputEntity.Properties.Remove(fieldName);

            if (attributeValue != null)
                inputEntity.Properties[fieldName] = attributeValue;
        }

        public string GetAttributeValue(string fieldName)
        {
            return GetAttributeValue(GetPropertyFromContext(fieldName), false, null);
        }

        public string GetAttributeValue(string fieldName, string format)
        {
            return GetAttributeValue(GetPropertyFromContext(fieldName), false, format);
        }

        public string GetAttributeValue(string fieldName, ImageType type)
        {
            return GetAttributeValue(GetPropertyFromContext(fieldName, type), false, null);
        }

        public string GetAttributeValue(string fieldName, ImageType type, string format)
        {
            return GetAttributeValue(GetPropertyFromContext(fieldName, type), false, format);
        }

        public string GetAttributeValue(object prop, bool textValue)
        {
            return GetAttributeValue(prop, textValue, null);
        }

        public string GetAttributeValue(object prop, bool textValue, string format)
        {
            if (prop == null)
                return null;

            Type propertyType = prop.GetType();
            string propertyValue = null;

            if (propertyType == typeof(string))
            {
                propertyValue = prop.ToString();
            }
            else if (propertyType == typeof(CrmDateTime))
            {
                if (format == null)
                    propertyValue = ((CrmDateTime)prop).Value.ToString();
                else
                    propertyValue = ((CrmDateTime)prop).UserTime.ToString(format);
            }
            else if (propertyType == typeof(Key))
            {
                propertyValue = ((Key)prop).Value.ToString("D");
            }
            else if (propertyType == typeof(Customer))
            {
                if (textValue)
                {
                    propertyValue = ((Customer)prop).name;
                }
                else
                {
                    propertyValue = ((Customer)prop).Value.ToString();
                }
            }
            else if (propertyType == typeof(Lookup))
            {
                Lookup luObject = ((Lookup)prop);

                if (textValue)
                {
                    propertyValue = luObject.name;

                    if (propertyValue == null)
                        propertyValue = GetPrimaryFieldValue(luObject.type, luObject.Value);
                }
                else
                {
                    propertyValue = ((Lookup)prop).Value.ToString("D");
                }
            }
            else if (propertyType == typeof(Owner))
            {
                if (textValue)
                {
                    propertyValue = ((Owner)prop).name;
                }
                else
                {
                    propertyValue = ((Owner)prop).Value.ToString("D");
                }
            }
            else if (propertyType == typeof(CrmBoolean))
            {
                propertyValue = ((CrmBoolean)prop).Value.ToString();
            }
            else if (propertyType == typeof(Picklist))
            {
                if (textValue)
                {
                    propertyValue = ((Picklist)prop).name.ToString();
                }
                else
                {
                    propertyValue = ((Picklist)prop).Value.ToString();
                }
            }
            else if (propertyType == typeof(CrmDecimal))
            {
                propertyValue = ((CrmDecimal)prop).Value.ToString();
            }
            else if (propertyType == typeof(CrmFloat))
            {
                propertyValue = ((CrmFloat)prop).Value.ToString();
            }
            else if (propertyType == typeof(CrmMoney))
            {
                propertyValue = ((CrmMoney)prop).Value.ToString();
            }
            else if (propertyType == typeof(CrmNumber))
            {
                propertyValue = ((CrmNumber)prop).Value.ToString();
            }
            else if (propertyType == typeof(Status))
            {
                if (textValue)
                {
                    propertyValue = ((Status)prop).name.ToString();
                }
                else
                {
                    propertyValue = ((Status)prop).Value.ToString();
                }
            }

            return propertyValue;

        }

        public object SetAttributeValue(object prop, string value)
        {
            if (value == null || value == string.Empty)
                return null;

            if (prop == null)
                return value.ToString();

            Type propertyType = prop.GetType();
            if (prop == typeof(string))
            {
                return value.ToString();
            }
            else if (propertyType == typeof(CrmDateTime))
            {
                ((CrmDateTime)prop).Value = value;
            }
            else if (propertyType == typeof(Key))
            {
                ((Key)prop).Value = new Guid(value);
            }
            else if (propertyType == typeof(Customer))
            {
                ((Customer)prop).Value = new Guid(value);
            }
            else if (propertyType == typeof(Lookup))
            {
                ((Lookup)prop).Value = new Guid(value);
            }
            else if (propertyType == typeof(Owner))
            {
                ((Owner)prop).Value = new Guid(value);
            }
            else if (propertyType == typeof(CrmBoolean))
            {
                ((CrmBoolean)prop).Value = bool.Parse(value);
            }
            else if (propertyType == typeof(Picklist))
            {
                ((Picklist)prop).Value = int.Parse(value);
            }
            else if (propertyType == typeof(CrmDecimal))
            {
                ((CrmDecimal)prop).Value = decimal.Parse(value);
            }
            else if (propertyType == typeof(CrmFloat))
            {
                ((CrmFloat)prop).Value = float.Parse(value);
            }
            else if (propertyType == typeof(CrmMoney))
            {
                ((CrmMoney)prop).Value = decimal.Parse(value);
            }
            else if (propertyType == typeof(CrmNumber))
            {
                ((CrmNumber)prop).Value = int.Parse(value);
            }
            else if (propertyType == typeof(Status))
            {
                ((Status)prop).Value = int.Parse(value);
            }

            return prop;

        }

        public string GetPrimaryFieldValue(string entityName, Guid objectId)
        {
            IMetadataService metaData = GetMetadataService();
            ICrmService crmService = GetCrmService();

            // Get the metadata about the current entity.
            RetrieveEntityRequest req = new RetrieveEntityRequest();
            req.EntityItems = Microsoft.Crm.Sdk.Metadata.EntityItems.IncludeAttributes;
            req.LogicalName = entityName;
            RetrieveEntityResponse res = (RetrieveEntityResponse)metaData.Execute(req);

            BusinessEntityCollection col = GetDynamicEntitiesWithFiltersOrdered(
                entityName,
                new string[] { res.EntityMetadata.PrimaryField },
                new string[] { res.EntityMetadata.PrimaryKey },
                new string[] { objectId.ToString() },
                new string[] { "createdon" },
                new bool[] { true }
            );

            if (col.BusinessEntities.Count > 0)
            {
                DynamicEntity obj = ((DynamicEntity)col.BusinessEntities[0]);
                if (obj.Properties.Contains(res.EntityMetadata.PrimaryField))
                    return obj.Properties[res.EntityMetadata.PrimaryField].ToString();
            }

            return null;
        }

         /// <summary>
         /// http://crmscape.blogspot.com/2009/02/dynamic-entity-and-entity-query-helper.html
         /// </summary>
         /// <param name="crmService"></param>
         /// <param name="entityName"></param>
         /// <param name="attributes"></param>
         /// <param name="fields"></param>
         /// <param name="values"></param>
         /// <param name="orderfields"></param>
         /// <param name="orderAscending"></param>
         /// <returns></returns>
        public BusinessEntityCollection GetDynamicEntitiesWithFiltersOrdered(string entityName, string[] attributes, string[] fields, string[] values, string[] orderfields, bool[] orderAscending)
        {
            try
            {
                var filters = new FilterExpression { FilterOperator = LogicalOperator.And };

                for (int i = 0; i < fields.Length; i++)
                {
                    var condition = new ConditionExpression
                    {
                        AttributeName = fields[i],
                        Operator = ConditionOperator.Equal,
                        Values = new[] { values[i] }
                    };

                    filters.Conditions.Add(condition);
                }

                QueryExpression query = null;
                if (attributes != null)
                {
                    query = new QueryExpression
                    {
                        EntityName = entityName,
                        ColumnSet = new ColumnSet(attributes),
                        Criteria = filters
                    };
                }
                else
                {
                    query = new QueryExpression
                    {
                        EntityName = entityName,
                        ColumnSet = new AllColumns(),
                        Criteria = filters
                    };
                }

                if (orderfields != null & orderAscending != null)
                {
                    for (int i = 0; i < orderfields.Length; i++)
                    {
                        var oneExpression = new OrderExpression
                        {
                            AttributeName = orderfields[i],
                            OrderType = (orderAscending[i] ? OrderType.Ascending : OrderType.Descending)
                        };

                        query.Orders.Add(oneExpression);
                    }
                }

                var retrieve = new RetrieveMultipleRequest { Query = query, ReturnDynamicEntities = true };

                var retrieved = (RetrieveMultipleResponse)GetCrmService().Execute(retrieve);

                return retrieved.BusinessEntityCollection;
            }
            catch (Exception ex)
            {
                throw new InvalidPluginExecutionException(
                   String.Format("An error occurred in GetDynamicEntitiesWithFilters plug-in. {0} - trace - {1} -  source - {2}", ex.Message, ex.StackTrace, ex.Source), ex);
            }
        }
    }
}
